﻿using System.Collections.Generic;
using System.Linq;

namespace Pvf.Core
{
    internal sealed class PvfFileExplorer : IFileExplorer
    {
        private readonly IFileContainer _root;
        public PvfFileExplorer(IPvfEnv env)
        {
            _root = new PvfFileContainer(env);
            Env = env;
        }

        public IPvfEnv Env { get; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="addTo"></param>
        /// <param name="file"></param>
        public void Add(string addTo, IFile file)
        {
            var split = addTo.Split('/');
            var index = 0;

            AddToFolder(split, file, null, ref index);
        }

        public void Foreach(System.Action<IFile> action)
        {
            ForeachPrivate(_root, action);
        }

        private void ForeachPrivate(IFileContainer root, System.Action<IFile> action)
        {
            if (root == null)
            {
                return;
            }

            foreach (var item in root.Items)
            {
                if (item is IFileContainer fileContainer)
                {
                    ForeachPrivate(fileContainer, action);
                }
                else
                {
                    action?.Invoke(item);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="convertSplitChar"></param>
        /// <returns></returns>
        public T Find<T>(string path, bool convertSplitChar) where T : IFile
        {
            if (string.IsNullOrEmpty(path))
            {
                return default;
            }

            if (convertSplitChar)
            {
                path = path.Replace('\\', '/');
            }

            var split = path.Split('/');

            if (split.Length == 0)
            {
                return default;
            }

            var index = 0;
            return (T)FindFile(split, ref index, _root);
        }

        private static IFile FindFile(IReadOnlyList<string> split, ref int index, IFileContainer fileContainer)
        {
            while (true)
            {
                if (index >= split.Count)
                {
                    break;
                }

                var name = split[index];
                if (index == split.Count - 1)
                {
                    //表示上一级
                    if (name == "..")
                    {
                        return fileContainer is IFile gameFileBase ? gameFileBase.Parent as IFile : null;
                    }
                    var found = fileContainer.Get<IFile>(name);
                    if (found != null)
                    {
                        return found;
                    }
                }
                else
                {
                    //表示上一级
                    if (name == "..")
                    {
                        if (fileContainer is IFile gameFileBase)
                        {
                            fileContainer = gameFileBase.Parent;
                        }
                        else
                        {
                            return null;
                        }

                        if (fileContainer == null)
                        {
                            return null;
                        }
                    }
                    else
                    {
                        var found = fileContainer.Get<IFileContainer>(name);

                        if (found == null)
                        {
                            return null;
                        }

                        fileContainer = found;
                    }
                }
                index++;
            }

            return null;
        }

        private void AddToFolder(IReadOnlyList<string> split, IFile file, IFile parentFile, ref int index)
        {
            while (true)
            {
                if (index >= split.Count)
                {
                    break;
                }

                var name = split[index];
                if (index == split.Count - 1)
                {
                    var fileContainer = parentFile is PvfFileContainer folder ? folder : _root;
                    fileContainer.Add(file);
                }
                else
                {
                    var fileContainer = parentFile is PvfFileContainer folder ? folder : _root;
                    var foundFolder = fileContainer.Get<IFileContainer>(name);

                    parentFile = foundFolder ?? fileContainer.Add(new PvfFileContainer(Env)
                    {
                        Name = name
                    });
                }
                index++;
            }
        }

        public string[] GetTopFileContainersName()
        {
            return _root.Items.Where(x => x is PvfFileContainer).Select(x => x.Name).ToArray();
        }

        public IFileContainer[] GetTopFileContainers()
        {
            return _root.Items.Where(x => x is PvfFileContainer).Cast<IFileContainer>().ToArray();

        }

        public string[] GetTopFilesName()
        {
            return _root.Items.Where(x => !(x is PvfFileContainer)).Select(x => x.Name).ToArray();
        }

        public IFile[] GetTopFiles()
        {
            return _root.Items.Where(x => !(x is PvfFileContainer)).ToArray();
        }
    }
}
